- 변수를 생성하면 변수는 어디에 저장되는가?
- 프로그램은 어떻게 변수를 찾는가?
- 식별자에 대한 유효범위
- 식별자 이름으로 변수를 찾기 위한 규칙의 집합
- 스코프는 두 가지 방식으로 작동한다.
- Lexical Scope : 일반적이고 다수의 프로그래밍 언어가 사용하는 방식
- 동적 스코프(Dynamic Scopy)
- 변수에 값을 대입하거나(LHS 참조) 변수의 값을 얻어오기 위해서(RHS 참조)
- js는 동적 언어 또는 인터프리터 언어로 분류하지만 사실은 컴파일러 언어이다..?
- 자바스크립트 엔진이 코드를 인터프리팅하기 전에 컴파일 한다.
- 문자열을 나누어 토큰 이라 불리는 의미 있는 조각으로 만드는 과정.
- 일반적인 언어의 컴파일러는 첫 단계를 토크나이징 또는 렉싱이라 불리는 작업으로 시작한다.
- 토큰 배열을 프로그램의 문법 구조를 반영하여 중첩 원소를 갖는 트리 형태로 바꾸는 과정이다.
- 파싱의 결과로 만들어진 트리를 AST(Abstract Syntax Tree 추상 구문 트리)라 부른다.
- 대입 연산의 방향
- 모두 현재 실행 중인 스코프에서 시작한다.
- LHS 검색은 값을 넣어야 하므로 변수 컨테이너 자체를 찾는다.
- RHS 검색은 단순히 특정 변수의 값을 찾는 것과 다를 바 없다.
- 어떤 변수의 첫 RHS 검색이 실패하면 다시는 변수를 찾을 수 없다. 이렇게 스코프에서 찾지 못한 변수는 ‘선언되지 않은 변수’라고 한다.
- 반면에 엔진이 LHS 검색을 수행하여 변수를 찾지 못하고 최상위 층(글로벌 스코프)에 도착할 때 프로그램이 ‘Strict Mode’로 동작하고 있는 것이 아니라면, 글로벌 스코프는 엔진이 검색하는 이름을 가진 새로운 변수를 생성해서 엔진에게 넘겨준다.
- 대상 변수를 현재 스코프에서 찾지 못하면 엔진은 다음 바깥의 스코프로 넘어가는 식으로 변수를 찾거나 글로벌 스코프라 부르는 가장 바깥 스코프에 도달할 때까지 계속한다.
- 어떤 함수가 어디서 또는 어떻게 호출되는지에 상관없이 함수의 렉시컬 스코프는 함수가 선언된 위치에 따라 정의된다.
- 변수와 함수 선언문 모두 코드가 실제 실행되기 전에 먼저 처리된다.
- 선언된 위치에서 코드의 꼭대기로 “끌어올려”진다.
- 선언문을 끌어올리는 동작을 호이스팅 이라고 한다.
- 스코프별로 작동한다.
- 먼저 함수가 끌어올려지고 다음으로 변수가 올려진다.
- 클로저는 함수가 속한 렉시컬 스코프를 기억하여 함수가 렉시컬 스코프 밖에서 실행될 때에도 이 스코프에 접근할 수 있게 하는 기능을 뜻한다.
- 클로저는 렉시컬 스코프에 의존해 코드를 작성한 결과로 그냥 발생한다. 모든 코드에서 클로저는 생성되고 사용된다.
var fn;
function foo() {
var a = 2;
function baz() {
console.log(a);
}
fn = baz;
}
function bar() {
fn();
}
foo();
bar();
- 어떤 방식으로 내부 함수를 자신이 속한 렉시컬 스코프 밖으로 수송하든 함수는 처음 선언된 곳의 스코프에 대한 참조를 유지한다. 즉 어디에서 해당 함수를 실행하든 클로저가 작용한다.
function wait(message) {
setTimeout(function timer() {
console.log(message);
}, 1000);
}
wait("hello");
timer
함수는 wait()
함수의 스코프에 대한 스코프 클로저를 가지고 있으므로 변수 message에 대한 참조를 유지하고 사용할 수 있다.
wait()
실행 1초 후, wait의 내부 스코프는 사라져야 하지만 익명의 함수가 여전히 해당 스코프에 대한 클로저를 가지고 있다.
클로저를 설명하는 가장 흔하고 표준적인 사례 : for 반복문
for (var i = 1; i <= 5; i++) {
setTimeout(function timer() {
console.log(i);
}, i * 1000);
}
- 클로저의 능력을 활용하면서 표면적으로는 콜백과 상관없는 코드 패턴들이 있다. 그중 가장 강력한 패턴이다.
function CoolModule() {
var something = "cool";
var another = [1, 2, 3];
function doSomething() {
console.log(something);
}
function doAnother() {
console.log(another.join("!"));
}
return {
doSomething: doSomething,
doAnother: doAnother,
};
}
var foo = CoolModule();
foo.doSomething();
foo.doAnother();
- 이들 모두
CoolModule()
의 내부 스코프를 렉시컬 스코프(당연히 클로저도 따라온다)로 가진다. - 이 코드와 같은 자바스크립트 패턴을 모듈이라고 부른다.